package ace.module.account.core.impl.service.authentication.handler.impl.phone;

import ace.cmp.core.exception.BusinessException;
import ace.cmp.json.api.JsonService;
import ace.module.account.api.enums.IamAccountPassportPassportTypeEnum;
import ace.module.account.api.model.auth.AuthenticationGrantType;
import ace.module.account.api.model.auth.dto.AuthenticationResult;
import ace.module.account.api.model.auth.input.GenericAuthenticationInputImpl;
import ace.module.account.api.model.auth.input.params.AuthenticationParamsPhoneCode;
import ace.module.account.api.model.config.IamApplicationCaptchaSmsAppConfig;
import ace.module.account.core.impl.dao.entity.IamAccountPassport;
import ace.module.account.core.impl.manager.IamAccountPassportManager;
import ace.module.account.core.impl.manager.IamApplicationConfigManager;
import ace.module.account.core.impl.manager.cache.IamAuthenticationLoginCacheManager;
import ace.module.account.core.impl.service.authentication.handler.RegisterQuickManager;
import ace.module.account.core.impl.service.authentication.handler.impl.AbstractAuthenticationHandler;
import ace.module.account.core.impl.service.authentication.handler.model.input.AuthenticationHandlerContextInput;
import ace.module.captcha.sms.api.CaptchaSmsApi;
import java.util.Optional;
import org.springframework.stereotype.Component;

/**
 * @author caspar
 * @date 2023/3/16 9:49
 */
@Component
public class PhoneCaptchaSmsAuthenticationHandler
    extends AbstractAuthenticationHandler<
    AuthenticationParamsPhoneCode,
    GenericAuthenticationInputImpl<AuthenticationParamsPhoneCode>> {

  private final IamAccountPassportManager iamAccountPassportManager;
  private final IamApplicationConfigManager iamApplicationConfigManager;
  private final CaptchaSmsApi captchaSmsApi;
  private final IamAuthenticationLoginCacheManager iamAuthenticationLoginCacheManager;
  private final RegisterQuickManager registerQuickManager;

  protected PhoneCaptchaSmsAuthenticationHandler(
      JsonService jsonService,
      IamAccountPassportManager iamAccountPassportManager,
      IamApplicationConfigManager iamApplicationConfigManager, CaptchaSmsApi captchaSmsApi,
      IamAuthenticationLoginCacheManager iamAuthenticationLoginCacheManager,
      RegisterQuickManager registerQuickManager) {
    super(AuthenticationGrantType.PHONE_NUMBER_CAPTCHA_SMS_AUTH.getCode(), jsonService);
    this.iamAccountPassportManager = iamAccountPassportManager;
    this.iamApplicationConfigManager = iamApplicationConfigManager;
    this.captchaSmsApi = captchaSmsApi;
    this.iamAuthenticationLoginCacheManager = iamAuthenticationLoginCacheManager;
    this.registerQuickManager = registerQuickManager;
  }

  @Override
  protected AuthenticationResult doAuthentication(
      AuthenticationHandlerContextInput<AuthenticationParamsPhoneCode> context) {
    String phone = context.getAuthenticationContext().getBizParams().getPhone();
    boolean registerQuickFlag = Boolean.TRUE.equals(context.getAuthenticationContext().getBizParams().getRegisterQuickFlag());

    this.checkCaptchaSms(context);

    IamAccountPassport accountPassport = this.getAccountPassport(context);

    // 生成支持快速注册sessionkey与保存
    registerQuickManager.ifSupportRegisterQuickThenAndThrow(
        accountPassport != null,
        registerQuickFlag,
        (sessionKey) -> {
          iamAuthenticationLoginCacheManager.setPhone(sessionKey, phone);
        });

    if (accountPassport == null) {
      throw new BusinessException("认证失败");
    }

    Long accountId = accountPassport.getIamAccountId();
    return AuthenticationResult.builder().accountId(accountId).build();
  }

  private IamAccountPassport getAccountPassport(AuthenticationHandlerContextInput<AuthenticationParamsPhoneCode> handlerContext) {
    Long iamAppId = handlerContext.getAuthenticationContext().getIamApplication().getId();
    String phoneNumber = handlerContext.getAuthenticationContext().getBizParams().getPhone();
    String tag = handlerContext.getAuthenticationContext().getTag();
    return iamAccountPassportManager
        .getByIamAppIdAndTagAndPassportAndPassportType(iamAppId, tag, phoneNumber, IamAccountPassportPassportTypeEnum.PHONE.getCode());
  }

  private void checkCaptchaSms(AuthenticationHandlerContextInput<AuthenticationParamsPhoneCode> handlerContext) {
    Long iamAppId = handlerContext.getAuthenticationContext().getIamApplication().getId();
    Optional<IamApplicationCaptchaSmsAppConfig> iamApplicationCaptchaSmsAppConfigOptional = Optional.ofNullable(
        this.iamApplicationConfigManager.getCaptchaSmsConfig(iamAppId)
    );

    Long captchaSmsAppId = iamApplicationCaptchaSmsAppConfigOptional
        .map(IamApplicationCaptchaSmsAppConfig::getCaptchaSmsAppId)
        .orElseThrow(() -> new BusinessException("请配置关联短信验证应用."));

    String code = handlerContext.getAuthenticationContext().getBizParams().getCode();
    String phoneNumber = handlerContext.getAuthenticationContext().getBizParams().getPhone();
    this.captchaSmsApi.verify(captchaSmsAppId, phoneNumber, code).check();
  }
}
